package dynamic

import (
	"fmt"
	"reflect"
	"sync"
)

var dynamicMap = map[string]interface{}{}

var dynamicMutex sync.RWMutex

func Register(structName string, serviceStruct interface{}) error {
	if len(structName) == 0 {
		return fmt.Errorf("dynamic register error: name is nil")
	}
	if serviceStruct == nil {
		return fmt.Errorf("dynamic register error: service is nil")
	}
	t := reflect.TypeOf(serviceStruct)
	if !(t.Kind() == reflect.Ptr || t.Kind() == reflect.Struct) {
		return fmt.Errorf("dynamic register error: service type must struct")
	}
	if reflect.ValueOf(serviceStruct).NumMethod() == 0 {
		return fmt.Errorf("dynamic register error: service method is nil")
	}
	dynamicMutex.RLock()
	_, ok := dynamicMap[structName]
	dynamicMutex.RUnlock()
	if ok {
		return fmt.Errorf("dynamic register error: already existing for name [%s]", structName)
	}
	dynamicMap[structName] = serviceStruct
	return nil
}

func Call(structName, method string, args ...interface{}) ([]interface{}, error) {
	if err := verify(structName, method); err != nil {
		return nil, err
	}
	inputs := make([]reflect.Value, len(args))
	for i, arg := range args {
		inputs[i] = reflect.ValueOf(arg)
	}
	dynamicMutex.Lock()
	defer dynamicMutex.Unlock()
	d := dynamicMap[structName]
	result := reflect.ValueOf(d).MethodByName(method).Call(inputs)
	out := make([]interface{}, len(result))
	for i, _ := range result {
		out[i] = result[i].Interface()
	}
	return out, nil
}

func verify(structName, method string) error {
	if len(structName) == 0 {
		return fmt.Errorf("dynamic verify error: name is nil")
	}
	if len(method) == 0 {
		return fmt.Errorf("dynamic verify error: method is nil")
	}
	d, ok := dynamicMap[structName]
	if !ok {
		return fmt.Errorf("dynamic verify error: don't exist for [%s]", structName)
	}
	m := reflect.ValueOf(d).MethodByName(method)
	if m.Kind() == reflect.Invalid {
		return fmt.Errorf("dynamic verify error: [%s] method invalid", method)
	}
	return nil
}
